package com.starsky.attendance;

import com.starsky.attendance.entity.UserAttendance;
import com.starsky.attendance.entity.UserAttendanceDto;
import com.starsky.common.easypoi.ExcelUtils;
import com.starsky.common.utils.ConvertUtils;
import com.starsky.common.utils.DateUtils;
import com.starsky.common.utils.ListUtils;
import com.starsky.common.utils.SwingAwtUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.util.CollectionUtils;

import javax.swing.*;
import javax.swing.text.JTextComponent;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.List;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @desc: excel操作考勤数据
 * 同一个人，同一天考勤数据保留一条，并按照从小到大，将同一人的数据排序。
 * 比如早上打卡，选择最早时间，下班打开选择最晚时间
 * <p>
 * 通过导入excel全量数据，通过计算汇总输出
 * 导入excel列包含：地区、姓名、账号、出入标识、出入日期(yyyy-MM-dd)、出入时间(hh:mm:ss)、员工号、部门名称
 * 输出信息：
 * 导入excel列包含：地区、姓名、账号、出入标识、员工号、部门名称、上班时间(yyyy-MM-dd hh:mm:ss)、下班时间(yyyy-MM-dd hh:mm:ss)
 * @author: wangsh
 * @time: 2021/4/15 9:34
 */

public class AttendanceApplication {

    public static void main(String[] args) {
        createJFrame();
    }

    /**
     * 创建面板，这个类似于 HTML 的 div 标签,我们可以创建多个面板并在 JFrame 中指定位置,
     * 面板中我们可以添加文本字段，按钮及其他组件。
     */
    public static void createJFrame() {
        //创建面板
        JPanel panel = new JPanel();
        //调用用户定义的方法并添加组件到面板
        addComponents(panel);

        //创建 JFrame 实例,将panel 面板添加到 JFrame中
        SwingAwtUtils.createJFrame("员工考勤", 600, 450, panel);
    }

    /**
     * 调用用户定义的方法并添加组件到面板
     */
    public static void addComponents(JPanel panel) {
        //布局部分我们这边不多做介绍,这边设置布局为 null
        panel.setLayout(null);
        panel.setPreferredSize(new Dimension(500, 350));//关键代码,设置JPanel的大小

        // 查询条件1：入用户账号文本域
        JLabel passwordLabel = SwingAwtUtils.createLabel("账号：", 10, 20, 80, 30);
        panel.add(passwordLabel);
        JTextField accountText = SwingAwtUtils.createTextField(20, 100, 20, 300, 30);
        panel.add(accountText);

        // 查询条件1：入用户账号文本域
        JLabel userNameLabel = SwingAwtUtils.createLabel("姓名：", 10, 55, 80, 30);
        panel.add(userNameLabel);
        JTextField userNameText = SwingAwtUtils.createTextField(20, 100, 55, 300, 30);
        panel.add(userNameText);

        // 查询条件2：导入excel
        //1.创建 JLabel
        panel.add(SwingAwtUtils.createLabel("选择导入Excel：", 10, 90, 120, 30));
        //2.创建文本域
        JTextField fromText = SwingAwtUtils.createTextField(20, 100, 90, 300, 30);
        panel.add(fromText);
        //3.创建选择按钮
        final JButton fromButton = SwingAwtUtils.createButton("选择", 420, 90, 80, 30);
        SwingAwtUtils.fileActionListener(fromButton, fromText, "xls,xlsx");
        panel.add(fromButton);

        //查询条件3：导出excel
        //1.创建 JLabel
        panel.add(SwingAwtUtils.createLabel("选择保存位置：", 10, 125, 120, 30));
        //2.创建文本域（文本域用于记录保存路径）
        JTextField toText = SwingAwtUtils.createTextField(20, 100, 125, 300, 30);
        panel.add(toText);
        //3.创建选择按钮
        final JButton toButton = SwingAwtUtils.createButton("选择", 420, 125, 80, 30);
        SwingAwtUtils.dirActionListener(toButton, toText);
        panel.add(toButton);

        //操作按钮：查询按钮
        final JButton button = SwingAwtUtils.createButton("查询", 100, 160, 80, 30);
        panel.add(button);
        // 创建清空按钮
        final JButton cleanButton = SwingAwtUtils.createButton("清空", 280, 160, 80, 30);
        panel.add(cleanButton);

        //日志显示框
        //1.创建 JLabel
        panel.add(SwingAwtUtils.createLabel("操作记录：", 10, 200, 120, 30));
        final JTextArea textArea = SwingAwtUtils.createTextArea("操作记录：", 100, 200, 400, 150);
        panel.add(textArea);

        //选择确定按钮的监听事件
        parseExcel(button, accountText, userNameText, fromText, toText, textArea);

        //选择清空按钮监听事件，清空日志显示框
        JTextComponent[] texts = {fromText, toText, accountText, textArea};
        SwingAwtUtils.cleanActionListener(cleanButton, texts);
    }

    /**
     * 加密、解密按钮的监听事件
     *
     * @param button：操作按钮
     * @param fromText：输入信息
     * @param toText：输出信息
     * @param accountText：用账号
     * @param textArea：日志信息文本框
     */
    public static void parseExcel(JButton button, JTextField accountText,
                                  JTextField userNameText,
                                  JTextField fromText,
                                  JTextField toText,
                                  JTextArea textArea) {
        //选择加密按钮的监听事件
        button.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                String fromPath = fromText.getText();
                String toPath = toText.getText();
                String account = accountText.getText();
                String userName = userNameText.getText();
                if (checkParams(fromPath, toPath)) {
                    return;
                }

//                String toJarPath = toText.getText() + "\\encrypt" + getNowDateTime() + ".jar";
                toPath = SwingAwtUtils.getFilePath(toPath, SwingAwtUtils.getFileName(fromPath));
                //打印输入日志
                StringBuilder builder = new StringBuilder();
                builder.append("统一认证号： " + account + "\n");
                builder.append("姓名： " + userName + "\n");
                builder.append("导入Excel： " + fromPath + "\n");
                builder.append("保存位置： " + toPath + "\n");

                String str = builder.toString();
                System.out.println(str);
                textArea.setText(str);

                //解析excel加密文件
                try {
                    final FileInputStream in = new FileInputStream(new File(fromPath));
                    final FileOutputStream out = new FileOutputStream(new File(toPath));
                    final List<UserAttendance> list = ExcelUtils.importExcel(in, 0, 1, UserAttendance.class);

                    //1.将时间拼接到一起，组成格式为yyyy-MM-dd  HH:mm:ss
                    List<UserAttendanceDto> targetList = filterData(list, account, userName);
                    //2.按照用户账号、出入时间（yyyy-mm-dd）分组，分别计算上班、下班时间
                    List<UserAttendanceDto> resList = handlerData(targetList);
                    //导出指定位置
                    ExcelUtils.exportExcel(out, resList, UserAttendanceDto.class);
                } catch (FileNotFoundException ex) {
                    throw new RuntimeException(ex);
                }
                textArea.append(" 查询成功");
            }
        });
    }

    /**
     * 处理考勤数据
     * 1.将时间拼接到一起，组成格式为yyyy-MM-dd  HH:mm:ss
     *
     * @param list
     * @return
     */
    public static List<UserAttendanceDto> filterData(List<UserAttendance> list, String account, String userName) {
        List<UserAttendanceDto> resList = new ArrayList<>();
        if (CollectionUtils.isEmpty(list)) {
            System.out.println("出入日期、出入时间不为空的考勤数据为空");
            return resList;
        }

        //过滤满足查询条件数据；不满足，则废弃
        if (StringUtils.isNotBlank(account) && StringUtils.isNotBlank(userName)) {
            list = list.stream().filter(item -> filterData(account, item.getUserAccount())
                    && filterData(userName, item.getUserName())).collect(Collectors.toList());
        } else if (StringUtils.isNotBlank(account)) {
            list = list.stream().filter(item -> filterData(account, item.getUserAccount())).collect(Collectors.toList());
        } else if (StringUtils.isNotBlank(userName)) {
            list = list.stream().filter(item -> filterData(userName, item.getUserName())).collect(Collectors.toList());
        }

        //将时间拼接到一起，组成格式为yyyy-MM-dd  HH:mm:ss
        List<UserAttendanceDto> targetList = new ArrayList<>();
        for (UserAttendance userAttendance : list) {
            String inOutDate = userAttendance.getInOutDate();
            String inOutTime = userAttendance.getInOutTime();
            if (StringUtils.isBlank(inOutDate) || StringUtils.isBlank(inOutTime)) {
                final String str = "用户： " + userAttendance.getUserName() + "[" + userAttendance.getUserAccount() + "], 出入日期，出入时间为空";
                System.out.println(str);
                continue;
            }

            String date = inOutDate.trim() + " " + inOutTime.trim();
//            String inOutDateStr = DateUtils.format(inOutDate, DateUtils.YYYY_MM_DD);
//            String inOutTimeStr = DateUtils.format(inOutTime, DateUtils.HHMMSS);
            final Date date2 = DateUtils.parseDate(date, DateUtils.YMDHMS);
            userAttendance.setCurrentTime(date);

            final UserAttendanceDto attendanceDto = ConvertUtils.sourceToTarget(userAttendance, UserAttendanceDto.class);
            attendanceDto.setInOutDate(inOutDate);
            attendanceDto.setCurrentTime(date2);
            targetList.add(attendanceDto);
        }
        return targetList;
    }

    /**
     * 查询条件与excel中的名称不一致，则不需要改数据
     *
     * @param searchParam
     * @param excelParam
     * @return
     */

    public static boolean filterData(String searchParam, String excelParam) {
        if (StringUtils.isBlank(excelParam)) {
            //System.out.println("excel导入用户账号、名称为空");
            return false;
        }
        if (searchParam.equals(excelParam)) {
            //System.out.println("excel导入用户账号、名称不为空，不满足查询条件");
            return true;
        }
        return false;
    }

    /**
     * 处理考勤数据
     * 1.将时间拼接到一起，组成格式为yyyy-MM-dd  HH:mm:ss
     * 2.按照用户账号、出入时间（yyyy-mm-dd）分组，分别计算上班、下班时间
     *
     * @param list
     * @return
     */
    public static List<UserAttendanceDto> handlerData(List<UserAttendanceDto> list) {

        List<UserAttendanceDto> resList = new ArrayList<>();
        //2.按照用户账号、出入时间（yyyy-mm-dd）分组，分别计算上班、下班时间
        if (CollectionUtils.isEmpty(list)) {
            System.out.println("用户出入日期、出入时间不为空的考勤数据为空");
            return resList;
        }

        //按照用户账号分组
        final Map<String, List<UserAttendanceDto>> listMap = list.stream()
                .collect(Collectors.groupingBy(item -> item.getUserAccount()));
        for (String userAccount : listMap.keySet()) {
            List<UserAttendanceDto> userList = new ArrayList<>();
            final List<UserAttendanceDto> userAttendanceList = listMap.get(userAccount);
            if (CollectionUtils.isEmpty(userAttendanceList)) {
                continue;
            }

            Map<String, List<UserAttendanceDto>> listMap2 = userAttendanceList.stream()
                    .sorted(Comparator.comparing(UserAttendanceDto::getInOutDate))
                    .collect(Collectors.groupingBy(item -> item.getInOutDate()));
            for (String time : listMap2.keySet()) {
                final List<UserAttendanceDto> userAttendanceDtos = listMap2.get(time);
                if (CollectionUtils.isEmpty(userAttendanceDtos)) {
                    continue;
                }

                ListUtils.asc(userAttendanceDtos, "currentTime");

                //顺序排列，第一个为上班打卡时间
                final UserAttendanceDto attendanceDto = userAttendanceDtos.get(0);
                //出入时间(yyyy-MM-dd hh:mm:ss)
                final Date workingTime = attendanceDto.getCurrentTime();
                attendanceDto.setInOutDate(DateUtils.format(workingTime, DateUtils.YYYY_MM_DD));
                attendanceDto.setWorkingTime(DateUtils.format(workingTime, DateUtils.HHMMSS));

                //顺序排列，最后一个为下班打卡时间
                Date offDutyTime = userAttendanceDtos.get(userAttendanceDtos.size() - 1).getCurrentTime();
                attendanceDto.setOffDutyTime(DateUtils.format(offDutyTime, DateUtils.HHMMSS));
                //考勤状态（早上8.30,下班17.30）,正常；异常
                //早上打卡时间早于8.30 为正常，下班打卡时间晚于17.30 为正常，
                String workStatus = getWorkStatus(workingTime, offDutyTime, attendanceDto.getInOutDate());
                attendanceDto.setWorkStatus(workStatus);

                //加班时长，下班18.00以后为加班，早上8.30以前为加班
                //早上(00:00:00) - 08:30:00之间
//                String hours = getWorkOvertime(workingTime, offDutyTime, attendanceDto.getInOutDate());
                String hours = getWorkOvertime(userAttendanceDtos, attendanceDto.getInOutDate());
                attendanceDto.setWorkOvertime(hours);
                userList.add(attendanceDto);
            }

            if (!CollectionUtils.isEmpty(userList)) {
                ListUtils.asc(userList, "inOutDate");
                resList.addAll(userList);
            }
        }
        return resList;
    }

    /**
     * @param list
     * @param inOutDate:出入时间(hh:mm:ss)， morning-上午；afternoon-下午
     * @return
     */
    private static String getWorkOvertime(List<UserAttendanceDto> list, String inOutDate) {
        double hours = 0D;
        if (CollectionUtils.isEmpty(list)) {
            return "";
        }

        List<UserAttendanceDto> morningList = new ArrayList<>();
        List<UserAttendanceDto> afternoonList = new ArrayList<>();
        list.forEach(item -> {
            final Date workingTimeTemp = item.getCurrentTime();
            //上午
            //早上(00:00:00) - 08:30:00之间
            Date startTime = DateUtils.parseDate(item.getInOutDate() + " 08:30:00", DateUtils.YMDHMS);
            if (workingTimeTemp.before(startTime)) {
                //工作时间在8.30之前
                morningList.add(item);
            }

            //晚上18:00:00 - 23:59:59之间
            Date endTime1 = DateUtils.parseDate(item.getInOutDate() + " 18:00:00", DateUtils.YMDHMS);
            if (workingTimeTemp.after(endTime1)) {
                //工作时间在18:00:00之后
                afternoonList.add(item);
            }
        });

        if (!CollectionUtils.isEmpty(morningList)) {
            //上午,早上(00:00:00) - 08:30:00之间，工作时间在8.30之前，取最大时间-取最小时间
            ListUtils.asc(morningList, "currentTime");
            //考勤状态（早上8.30,下班17.30）,正常；异常
            Date startTime = morningList.get(0).getCurrentTime();
            Date endTime = morningList.get(morningList.size() - 1).getCurrentTime();
            //加班时长，下班18.00以后为加班，早上8.30以前为加班
            //早上(00:00:00) - 08:30:00之间
            if (startTime.before(endTime)) {
                hours = (endTime.getTime() - startTime.getTime()) / (double) (1000 * 60 * 60);
            }
        }

        if (!CollectionUtils.isEmpty(afternoonList)) {
            //下午,晚上18:00:00 - 23:59:59之间，工作时间在18:00:00之后，取第大时间
            ListUtils.desc(afternoonList, "currentTime");
            //晚上18:00:00 - 23:59:59之间
            Date endTime1 = afternoonList.get(0).getCurrentTime();
            Date startTime = DateUtils.parseDate(inOutDate + " 18:00:00", DateUtils.YMDHMS);
            if (startTime.before(endTime1)) {
                hours += (endTime1.getTime() - startTime.getTime()) / (double) (1000 * 60 * 60);
            }
        }

        BigDecimal bigDecimal = new BigDecimal(hours + "");
        bigDecimal = bigDecimal.setScale(2, RoundingMode.HALF_UP);
        return bigDecimal.toString();
    }


    /**
     * 上午，加班时长，下班18.00以后为加班，早上8.30以前为加班
     * 早上(00:00:00) - 08:30:00之间
     * 晚上18:00:00 - 23:59:59之间
     *
     * @param startTime：上班开始时间
     * @param endTime：上班最晚时间
     * @param inOutDate
     * @return
     */
    public static String getMorningWorkOvertime(Date startTime, Date endTime, String inOutDate) {
        //考勤状态（早上8.30,下班17.30）,正常；异常
        //加班时长，下班18.00以后为加班，早上8.30以前为加班
        //早上(00:00:00) - 08:30:00之间
        double hours = 0D;
        if (startTime.before(endTime)) {
            hours = (endTime.getTime() - startTime.getTime()) / (double) (1000 * 60 * 60);
        }

        BigDecimal bigDecimal = new BigDecimal(hours + "");
        bigDecimal = bigDecimal.setScale(2, RoundingMode.HALF_UP);
        return bigDecimal.toString();
    }


    /**
     * 下午，加班时长，下班18.00以后为加班，早上8.30以前为加班
     * 早上(00:00:00) - 08:30:00之间
     * 晚上18:00:00 - 23:59:59之间
     *
     * @param endTime：下班最晚时间
     * @param inOutDate
     * @return
     */
    public static String getAfternoonWorkOvertime(Date endTime, String inOutDate) {
        //考勤状态（早上8.30,下班17.30）,正常；异常
        double hours = 0D;
        //晚上18:00:00 - 23:59:59之间
        Date startTime = DateUtils.parseDate(inOutDate + " 18:00:00", DateUtils.YMDHMS);
        if (startTime.before(endTime)) {
            hours += (endTime.getTime() - startTime.getTime()) / (double) (1000 * 60 * 60);
        }

        BigDecimal bigDecimal = new BigDecimal(hours + "");
        bigDecimal = bigDecimal.setScale(2, RoundingMode.HALF_UP);
        return bigDecimal.toString();
    }

    /**
     * 加班时长，下班18.00以后为加班，早上8.30以前为加班
     * 早上(00:00:00) - 08:30:00之间
     * 晚上18:00:00 - 23:59:59之间
     *
     * @param workingTime
     * @param offDutyTime
     * @param inOutDate
     * @return
     */
    public static String getWorkOvertime(Date workingTime, Date offDutyTime, String inOutDate) {
        //考勤状态（早上8.30,下班17.30）,正常；异常
        Date startTime = DateUtils.parseDate(inOutDate + " 08:30:00", DateUtils.YMDHMS);
        //加班时长，下班18.00以后为加班，早上8.30以前为加班
        //早上(00:00:00) - 08:30:00之间
        double hours = 0D;
        if (workingTime.before(startTime)) {
            hours = (startTime.getTime() - workingTime.getTime()) / (double) (1000 * 60 * 60);
        }
        //晚上18:00:00 - 23:59:59之间
        Date endTime1 = DateUtils.parseDate(inOutDate + " 18:00:00", DateUtils.YMDHMS);
        if (offDutyTime.after(endTime1)) {
            hours += (offDutyTime.getTime() - endTime1.getTime()) / (double) (1000 * 60 * 60);
        }

        BigDecimal bigDecimal = new BigDecimal(hours + "");
        bigDecimal = bigDecimal.setScale(2, RoundingMode.HALF_UP);
        return bigDecimal.toString();
    }

    /**
     * 考勤状态（早上8.30,下班17.30）,正常；异常
     *
     * @param workingTime
     * @param offDutyTime
     * @param inOutDate
     * @return
     */
    public static String getWorkStatus(Date workingTime, Date offDutyTime, String inOutDate) {
        //考勤状态（早上8.30,下班17.30）,正常；异常
        Date startTime = DateUtils.parseDate(inOutDate + " 08:30:00", DateUtils.YMDHMS);
        Date endTime = DateUtils.parseDate(inOutDate + " 17:30:00", DateUtils.YMDHMS);

        //早上打卡时间早于8.30 为正常，下班打卡时间晚于17.30 为正常，
        if (workingTime.before(startTime) && offDutyTime.after(endTime)) {
            //当前面的时间 workingTime 早于后面的时间 startTime 时返回true,否则返回false
            //当前面的时间offDutyTime 晚于后面的时间endTime 时返回true,否则返回false.
            return "正常";
        } else {
            return "异常";
        }
    }

    /**
     * 校验参数
     *
     * @param fromJarPath
     * @param toJarPath
     * @return
     */
    public static boolean checkParams(String fromJarPath, String toJarPath) {
        if (StringUtils.isEmpty(fromJarPath)) {
            SwingAwtUtils.showErrorDialog("jar文件不能为空!");
            return true;
        }
        if (StringUtils.isEmpty(toJarPath)) {
            SwingAwtUtils.showErrorDialog("保存路径不能为空!");
            return true;
        }
        return false;
    }


}
